package com.lt.lumentracks.config;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lt.lumentracks.constant.RedisConstant;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

// SpringCache配置类
// 在这里配置Bean的话，yml里面的配置会无效，只会使用这里的配置
// 如果不想用这种配置方式的话，在要序列化的类里面实现Serializable接口表明该类可序列化即可（最好顺便自己手动声明序列化版本UID）
@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory, ObjectMapper om) {

        // 启用“默认类型”信息，这样 Jackson 会往 JSON 里多写 @class，反序列化时 Jackson 就能准确还原成 Result 对象，不再出现 LinkedHashMap 问题

        // Jackson 2.10 之前（目前已弃用）
//        ObjectMapper cacheMapper = om.copy()
//                .enableDefaultTyping(
//                        ObjectMapper.DefaultTyping.NON_FINAL,
//                        JsonTypeInfo.As.PROPERTY
//                );

        // Jackson 2.10 及以后（推荐）
        ObjectMapper cacheMapper = om.copy() // 创建它的副本，目的是不影响原始配置（避免全局修改导致其他模块的序列化行为异常），同时允许自定义当前配置类的ObjectMapper行为。
                // activateDefaultTyping方法是解决“泛型类型丢失”的关键配置。它的作用是在JSON中添加类型信息，让反序列化时能准确还原对象的具体类型。
                // 默认情况下，Jackson在序列化对象时，会忽略泛型类型信息（如List<Result>中的Result类），导致反序列化时只能得到LinkedHashMap（无法转换为具体对象）。
                .activateDefaultTyping(
                        om.getPolymorphicTypeValidator(),      // 默认类型验证器。用于确保类型安全，防止恶意构造的@class字段触发任意类加载（避免反序列化漏洞，如RCE攻击）
                        ObjectMapper.DefaultTyping.NON_FINAL,  // 作用范围。指定哪些类型的对象需要添加类型信息。NON_FINAL表示所有非final类
                        JsonTypeInfo.As.PROPERTY               // 类型信息写入方式。指定类型信息在JSON中的存储位置，PROPERTY表示作为JSON对象的一个属性（默认属性名为@class）
                );

        // 1. 定义默认配置（全局生效）
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10))   // 统一过期时间，可省略
                // 指定键的序列化方式
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                // 指定值的序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer(cacheMapper)));

        // 2. 创建一个Map，为特定的缓存名（cacheNames）指定独立的配置（如单独设置过期时间）
        Map<String, RedisCacheConfiguration> specificConfigs = new HashMap<>();
        specificConfigs.put(RedisConstant.POST_RAW_DETAIL_KEY, config.entryTtl(Duration.ofMinutes(5L))); // 帖子详情的缓存时间短一点，毕竟其点赞与收藏可能会变化得很频繁

        // 3. 构建 CacheManager，同时应用默认配置和特定配置
        return RedisCacheManager.builder(factory)
                .cacheDefaults(config) // 设置默认配置
                .withInitialCacheConfigurations(specificConfigs) // 加载特定配置
                .build();
    }

}